#include "encode_url.hpp"
#include "http_parser.hpp"
#include "http_client.hpp"

#include "base64.hpp"
#include "hmac.hpp"
using namespace crypto;

IMPL_LOGGER(HttpParser, logger);

HttpParser::HttpParser() : AbstractParser()
{
	_handle_map.insert(std::make_pair(P_TENCENT_GET_USER_INFO, tencent_get_user_info));
}

int HttpParser::encode(ListBuffer & listbuffer, void * data)
{
	SSTencentPlatReq & req = ((Request*)data)->_req;

	HandleMap_Iter iter = _handle_map.find(req._type);
	if (_handle_map.end() == iter)
	{
		LOG4CPLUS_ERROR(logger, "can't handle cmd type. type: " << (int)req._type);
		return -1;
	}

	return iter->second(listbuffer, req);
}

int HttpParser::decode(ListBuffer & listbuffer, void *& data)
{
	std::string response;
	if (!frame(listbuffer, response))
	{
		return 0;
	}

	SSTencentPlatRes * res = new (_buffer) SSTencentPlatRes;
	// delete the end '}' char
	response[response.size()] = '\0';
	parser_simple_json(response, res->_key_val_map);

	return 0;
}

int HttpParser::parser_simple_json(const std::string & response, KeyValMap & key_val)
{
	size_t npos = response.find("\r\n\r\n");
	npos += strlen("\r\n\r\n{");

	std::vector<std::string> str_list;
	Utility::split_string(response, npos, ",", str_list);

	std::string key, val;

	std::vector<std::string>::iterator iter = str_list.begin();	
	for (; iter != str_list.end(); ++ iter)
	{
		size_t i = iter->find('\"', 1);
		if (std::string::npos == i)
		{
			continue;
		}

		key      = iter->substr(1, i - 1);
		
		size_t j = iter->find('\"', i + 2);

		if (std::string::npos == j)
		{
			val  = iter->substr(i + 2);
		}   
		else
		{
			val = iter->substr(j + 1, iter->size() - j - 2);
		}

		key_val.insert(std::make_pair(key, val));
	}

	return 0;
}

bool HttpParser::frame(ListBuffer & listbuffer, std::string & response)
{
	listbuffer.copy_out(response, -1);
	
	size_t npos = response.find("\r\n\r\n");

	if (std::string::npos == npos)
	{
		return false;
	}

	npos += strlen("\r\n\r\n");

	if (response.size() <= npos)
	{
		return false;
	}

	int brackets = 0;
	for (size_t i = npos; i < response.size(); ++ i)
	{
		if ('{' == response[i])
		{
			++ brackets;
		}
		else if ('}' == response[i])
		{
			-- brackets;
		}
	}

	if (0 != brackets)
	{
		return false;
	}

	assert((int32_t)response.size() == listbuffer.produce(response.size()));

	return true;
}

void HttpParser::tencent_sig(std::string & sig, const std::string & uri,
			const SSHttpClientRes & conf, KeyValMap & key_val)
{
	std::string info;
	info += "appid=" + conf._appid + "&openid=" + key_val["openid"] + \
			  "&openkey=" + key_val["openkey"] + "&pf=" + conf._pf;

	std::string query = std::string("GET&") + UrlParser::encode_uri(uri, "_") + \
						std::string("&") + UrlParser::encode_uri(info, "_");
	std::string key;
	key += conf._appkey + std::string("&");

	LOG4CPLUS_DEBUG(logger, "val: " << query << ", key: " << key);

	std::string hmac_sha1;
	Hmac::sha1(key.c_str(), key.size(), query.c_str(), query.size(), hmac_sha1);

	Base64::encode(hmac_sha1.c_str(), hmac_sha1.size(), sig);
}

int HttpParser::tencent_get_user_info(ListBuffer & listbuffer, SSTencentPlatReq & req)
{
	HttpClientConf & conf = HttpClient::instance()._conf;
	KeyValMap & key_val   = req._key_val_map;

	std::string uri;
	std::string get;

	uri = "/" + conf._version + "/user/get_info";

	std::string sig;
	tencent_sig(sig, uri, conf, key_val);

	get += "GET " + uri + "?openid=" + key_val["openid"] + \
			"&openkey=" + key_val["openkey"] + \
			"&appid=" + conf._appid + "&sig=" + sig + \
			"&pf=" + conf._pf + "&format=" + conf._format + \
			"&userip=" + key_val["userip"] + "\r\nHost: " + conf._url + "\r\n\r\n";

	LOG4CPLUS_DEBUG(logger, "request url: " << get);

	assert((int32_t)get.size() == listbuffer.copy_in_s(get.c_str(), get.size()));

	listbuffer.produce(get.size());

	return 0;
}

